/**
 * This object handler server functions.
 * Contains the functions to connect, listen, response the clients.
 * 
 * @author Marcelo A. Nagatomo - RA 062689
 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <time.h>
#include <unistd.h>

#include "ServerProperties.h"
#include "DataHandler.h"
#include "DebugUtils.h"

/**
 * Create the socket. 
 */
int Socket (int family, int type, int flags)	{
	int sockfd;
	
	if ((sockfd = socket(family, type, flags)) < 0) {
		perror("*** ERROR: an error occur while creating a client socket. ***\n");
		exit(1);

	} else
		return sockfd;
}

/** 
 * Assign a IP address at socket door created.
 */
void Bind (int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen){
	
	if (bind(sockfd, serv_addr, addrlen) < 0) {
		perror("*** ERROR: bind error. ***\n");
		exit(1);
	}
	return;
}


/**
 * Marks the socket referred to by 'listenfd' as a passive socket, that is, as a socket that will
 * be used to accept incoming connection requests using accept.
 */
void Listen (int sockfd, int backlog){

	if (listen(sockfd, backlog) < 0) {
		perror("*** ERROR: an error occur while creating a listener. ***\n");
		exit(1);
	}
	return;
}

/**
 * Get data and remove first connection of stack.
 */
int Accept(int sockfd, struct sockaddr *cliaddr, socklen_t *socklen){
	int connfd;
	if ((connfd = accept(sockfd, cliaddr, socklen)) < 0) {
		perror("accept");
		exit(1);
	}
	return connfd;
}

/**
 *  Creates a new process by duplicating the calling process.
 */
pid_t Fork(){
	pid_t processID;
	if ((processID = fork()) < 0){
		perror("*** ERROR: an error occur while fork(). ***\n");
		exit(1);
	}
	return processID;
}


/**
 *  Handles the request of client
 *
 *	@return char* response of bytes
 */
char* handleRequest(Request req){
	char* response;

	switch(req.option){
		case 1:
			response = getNumberMoviesList();
			break;
		case 2:
			response = getAllInfoMoviesList();
			break;
		case 3:
			response = getSinopseMovie(req.value);
			break;
		case 4:
			response = getAllInfoMovie(req.value);
			break;
		case 5:
			response = getMediumMovie(req.value);
			break;
		case 6:
			response = setRateMovie(req);
			break;
		default:
			response = NULL;
			break;
 	} 
	// debug
 	printResponse(response);
 			
	return response;
}


/**
 * Send a responses data
 *
 * @return int number of bytes sended or -1 when an error occur
 */
int sendResponse(int socketClient, char* response){
	int totalSize = 0;
	
	if( response != NULL && strlen(response) >0){
		int size = strlen(response);
		
		int numberOfMsgs = ( size + sizeof(int) ) / MAXLINE;
		if(numberOfMsgs != MAXLINE)
			numberOfMsgs++;
		
		char out[MAXLINE];
		sprintf(out, "%d", &numberOfMsgs);
		memcpy(out+sizeof(int), response, MAXLINE-sizeof(int));
		
		int i;
		for(i=0; i<numberOfMsgs;i++){
			memcpy(out, response, size);
		}
		
	}
	
	return totalSize;
}


/**
 * Handle the message received of client.
 *
 * @return char* command sended by client.
 */
char* HandleTcpClient(int socket){
 	char* buffer[REQUEST_SIZE];
	int size;
	
	size = read(socket, buffer, REQUEST_SIZE);
	//buffer[size] = '\0';
		
	if(size > 0){
		Request req;// = &buffer;
		memcpy(&req, buffer, REQUEST_SIZE);
	
		// {option, value, rate}
		printf("** DEBUG: ON **\n");
		printf("Option: [%d]\n", req.option);
		printf("Value: [%d]\n", req.value);
		printf("Rate: [%d]\n", req.rate);
		
		char *arr = handleRequest(req);
		return arr;
	} else {
		perror("*** ERROR: an error occur receive message. ***\n");
		exit(1);
		
	}
	
}



/**
 * 	TCP Server.
 *
 *	Description:
 *  - Receive a request of clients.
 * 	- Handle the action.
 * 	- Send response to client
 */
int main (int argc, char **argv) {
	pid_t pid;
	int port, serverSock, clientSock;
	struct sockaddr_in servaddr;	
	
	// set port to listen clients
	port = PORT;
	
	// initialize socket
	bzero(&servaddr, sizeof(servaddr));				// allocates the memory with '0'
	servaddr.sin_family      = AF_INET; 			// set Internet family
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 	// set IP address
	servaddr.sin_port        = htons(port);   		// set port
	
	// create server socket
	serverSock = Socket(AF_INET, SOCK_STREAM, 0);
	Bind(serverSock, (struct sockaddr *)&servaddr, sizeof(servaddr));
	Listen(serverSock, LISTENQ);
	
// 	FILE* fp = findDataSource("database.dat");
// 	MoviesList movies = getMovies(fp);
// 	printMovies(movies, 12, DEBUG);
	initDataHandler();

//  	Request req;
// 	req.value = 11;
// 	req.rate = 6;
// 	
// 	req.option = 1;
// 	char *arr = handleRequest(req);
// 	req.option = 2;
// 	arr = handleRequest(req);
// 	req.option = 3;
// 	arr = handleRequest(req);
// 	req.option = 4;
// 	arr = handleRequest(req);
// 	req.option = 5;
// 	arr = handleRequest(req);
// 	req.option = 6;
// 	arr = handleRequest(req);
	
	printf("> Sleeping thread and waiting connections...\n\n",pid);
	while(1){
		// wait a client
		clientSock = Accept(serverSock, (struct sockaddr *) NULL, NULL);
		
		// fork
		if ((pid = Fork()) == 0){
			close(serverSock);
			
			// handle tcp client request
			char* response = HandleTcpClient(clientSock);
		
 			// send message size
			int size = strlen(response);		
 			int bytes = send(clientSock, &size, sizeof(int), 0);
			
			// send message data
			bytes = send(clientSock, response, size, 0);
			
			printf("\n***\nReally sending:\n%d\n%s\n***\n\n", size, response);
						
			//	SendError Handling
			if(bytes == -1){
				perror("*** ERROR: send error. ***\n");
				exit(1);
			}
			
			printf("> Sleeping thread and waiting connections...\n\n",pid);
			close(clientSock);
			exit(0);
		}
		
		close(clientSock);
	}

	return 0;
}



